#include <qlib/geo/fan.h>

namespace qlib {

Fan::Fan() {
  radius = 0;
  tolerance = 0;
  rotation = 0;
  layer = 0;
  datatype = 0;
  tolerance = 0.01;

  box = Vec2dArray(2);

  point_num = 40;
}

Fan::Fan(Vec2d _center, double _radius, Vec2d _angle, float _rotation, double _tolerance, uint32_t _layer, uint32_t _datatype) {
  center = _center;
  radius = _radius;
  angle = _angle;
  rotation = _rotation;
  tolerance = _tolerance;
  layer = _layer;
  datatype = _datatype;
  rotation = 0;

  box = Vec2dArray(2);

  point_num = 40;
}

Fan::Fan(const Fan& oth) {
  operator=(oth);
}
Fan& Fan::operator=(const Fan& other) {
  point_num = other.point_num;
  center_angle = other.center_angle;
  center = other.center;
  radius = other.radius;
  angle = other.angle;
  tolerance = other.tolerance;
  rotation = other.rotation;
  layer = other.layer;
  datatype = other.datatype;
  repetition = other.repetition;

  properties.resize(other.properties.size());
  for (size_t i = 0; i < properties.size(); i++) {
    if (other.properties[i]) {
      properties[i] = Property::create();
      *properties[i] = *other.properties[i];
    }
  }

  polygon = to_polygon();

  return *this;
}

Fan::s_ptr Fan::copy() const {
  auto res = Fan::create();
  *res = *this;

  return res;
};

void Fan::init(Vec2d _center, double _radius, Vec2d _angle, float _rotation, double _tolerance, uint32_t _layer, uint32_t _datatype) {
  center = _center;
  radius = _radius;
  angle = _angle;
  rotation = _rotation;
  tolerance = _tolerance;
  layer = _layer;
  datatype = _datatype;

  point_num = 40;
}

void Fan::scale(double s) {
  radius *= s;
}

double getAngle(Vec2d p0, Vec2d p1) {
  auto cha = p1 - p0;
  double angle = atan2(cha.y, cha.x);
  return angle;
}

void Fan::mirror(Vec2d p0, Vec2d p1) {
  qlib::Polygon::s_ptr polygon = to_polygon();

  polygon->mirror(p0, p1);

  center = polygon->points[0];
  auto p0t = polygon->points[1];
  auto p1t = polygon->points.back();

  angle.y = getAngle(center, p0t);
  angle.x = getAngle(center, p1t);

  if (angle.y < angle.x) {
    angle.y += 2 * M_PI;
  }

  return;

  // 1. 镜像中心点
  const Vec2d AB = p1 - p0;
  const Vec2d AP = center - p0;
  const double AB_sq = AB.dot(AB);

  // 避免除零
  if (AB_sq < 1e-20)
    return;

  const double t = AP.dot(AB) / AB_sq;
  center = 2.0 * (p0 + AB * t) - center;

  // 2. 计算镜像轴角度（快速atan2）
  const double alpha = std::atan2(AB.y, AB.x);

  // 3. 镜像旋转角度（使用预计算的正余弦）
  const double new_rot = 2.0 * alpha - rotation;

  // 角度标准化到[-π, π]
  rotation = std::remainder(new_rot, 2.0 * M_PI);

  // Vec2d v = p1 - p0;
  // double tmp = v.length_sq();
  // if (tmp == 0)
  //   return;

  // Vec2d r = v * (2 / tmp);
  // Vec2d p2 = p0 * 2;

  // Vec2d* p = &center;
  // *p = v * (*p - p0).inner(r) - *p + p2;
}
void Fan::translate(double dx, double dy) {
  center += Vec2d(dx, dy);
}
void Fan::transform(double magnification, bool x_reflection, Vec2d origin) {
  if (x_reflection) {
    center.y *= -1;
  }
  radius *= magnification;
  center += origin;
}

Polygon::s_ptr Fan::to_polygon(bool is_tri) {
  Polygon::s_ptr result = Polygon::create();

  result->layer = layer;
  result->datatype = datatype;
  result->properties = properties;
  result->repetition = repetition;

  double radius_x = radius, radius_y = radius, inner_radius_x = 0, inner_radius_y = 0;
  double initial_angle = angle.x, final_angle = angle.y;

  const double full_angle = (final_angle == initial_angle) ? 2 * M_PI : fabs(final_angle - initial_angle);
  if (inner_radius_x > 0 && inner_radius_y > 0) {
    uint64_t num_points1 = 1 + arc_num_points(full_angle, radius_x > radius_y ? radius_x : radius_y, tolerance);
    if (num_points1 < GDSTK_MIN_POINTS)
      num_points1 = GDSTK_MIN_POINTS;
    uint64_t num_points2 = 1 + arc_num_points(full_angle, inner_radius_x > inner_radius_y ? inner_radius_x : inner_radius_y, tolerance);
    if (num_points2 < GDSTK_MIN_POINTS)
      num_points2 = GDSTK_MIN_POINTS;

    result->points.resize(num_points1 + num_points2);
    Vec2d* v = result->points.data();
    if (full_angle == 2 * M_PI) {
      // Ring
      for (uint64_t i = 0; i < num_points1; i++) {
        const double angle = i * 2 * M_PI / (num_points1 - 1);
        *v++ = center + Vec2{radius_x * cos(angle), radius_y * sin(angle)};
      }
      for (uint64_t i = num_points2; i > 0; i--) {
        const double angle = (i - 1) * 2 * M_PI / (num_points2 - 1);
        *v++ = center + Vec2{inner_radius_x * cos(angle), inner_radius_y * sin(angle)};
      }
    } else {
      // Ring slice
      double initial_ell_angle = elliptical_angle_transform(initial_angle, radius_x, radius_y);
      double final_ell_angle = elliptical_angle_transform(final_angle, radius_x, radius_y);
      for (uint64_t i = 0; i < num_points1; i++) {
        const double angle = LERP(initial_ell_angle, final_ell_angle, (double)i / (double)(num_points1 - 1));
        *v++ = center + Vec2{radius_x * cos(angle), radius_y * sin(angle)};
      }
      initial_ell_angle = elliptical_angle_transform(initial_angle, inner_radius_x, inner_radius_y);
      final_ell_angle = elliptical_angle_transform(final_angle, inner_radius_x, inner_radius_y);
      for (uint64_t i = num_points2; i > 0; i--) {
        const double angle = LERP(initial_ell_angle, final_ell_angle, (double)(i - 1) / (double)(num_points2 - 1));
        *v++ = center + Vec2{inner_radius_x * cos(angle), inner_radius_y * sin(angle)};
      }
    }
  } else {
    uint64_t num_points = 1 + arc_num_points(full_angle, radius_x > radius_y ? radius_x : radius_y, tolerance);
    if (num_points < GDSTK_MIN_POINTS)
      num_points = GDSTK_MIN_POINTS;
    if (full_angle == 2 * M_PI) {
      // Full ellipse
      result->points.resize(num_points);
      Vec2d* v = result->points.data();
      for (uint64_t i = 0; i < num_points; i++) {
        const double angle = i * 2 * M_PI / num_points;
        *v++ = center + Vec2{radius_x * cos(angle), radius_y * sin(angle)};
      }
    } else {
      // Slice
      const double initial_ell_angle = elliptical_angle_transform(initial_angle, radius_x, radius_y);
      const double final_ell_angle = elliptical_angle_transform(final_angle, radius_x, radius_y);

      num_points = point_num;

      result->points.resize(num_points + 1);
      Vec2d* v = result->points.data();
      *v++ = center;
      for (uint64_t i = 0; i < num_points; i++) {
        const double angle = LERP(initial_ell_angle, final_ell_angle, (double)i / (num_points - 1.0));
        *v++ = center + Vec2{radius_x * cos(angle), radius_y * sin(angle)};
      }

      center_angle = (final_ell_angle - initial_ell_angle) / M_PI * 180;
    }
  }

  result->rotate(rotation, center);

  if (is_tri) {
    polygon = result;
    polygon->genTriIndices();
  }

  return result;
}

Vec2dArray Fan::bounding_box() {
  Vec2dArray res(2);

  auto poly = to_polygon();
  res = poly->bounding_box();

  Vec2d& min = res[0];
  Vec2d& max = res[1];

  if (repetition.type != RepetitionType::None) {
    // 获取重复下的极值
    Vec2dArray offsets = {};
    repetition.get_extrema(offsets);

    Vec2d* off = offsets.data();
    Vec2d min0 = min;
    Vec2d max0 = max;

    // 将极值应用到每个包围盒上
    for (uint64_t i = offsets.size(); i > 0; i--, off++) {
      if (min0.x + off->x < min.x)
        min.x = min0.x + off->x;
      if (max0.x + off->x > max.x)
        max.x = max0.x + off->x;
      if (min0.y + off->y < min.y)
        min.y = min0.y + off->y;
      if (max0.y + off->y > max.y)
        max.y = max0.y + off->y;
    }
  }

  return res;
}

void Fan::rotate(double _angle, qlib::Vec2d _center) {
  double ca = cos(_angle);
  double sa = sin(_angle);

  Vec2d vt;

  vt = center - _center;
  center.x = vt.x * ca - vt.y * sa + _center.x;
  center.y = vt.x * sa + vt.y * ca + _center.y;

  rotation += _angle;
}

// 计算扇形圆弧上的端点和中点
Vec2d Fan::get_arc_center() {
  double startRad = angle.x;
  double endRad = angle.y;
  double midRad = (startRad + endRad) / 2.0;
  // 计算中点的坐标
  Vec2d midPoint;
  midPoint.x = center.x + radius * std::cos(midRad);
  midPoint.y = center.y + radius * std::sin(midRad);
  return midPoint;
}

// 计算扇形圆弧上的端点和中点
Vec2d Fan::get_arc_start() {
  double startRad = angle.x;
  // 计算起始端点的坐标
  Vec2d startPoint;
  startPoint.x = center.x + radius * std::cos(startRad);
  startPoint.y = center.y + radius * std::sin(startRad);
  return startPoint;
}

// 计算扇形圆弧上的端点和中点
Vec2d Fan::get_arc_end() {
  double endRad = angle.y;
  // 计算起始端点的坐标
  Vec2d endPoint;
  endPoint.x = center.x + radius * std::cos(endRad);
  endPoint.y = center.y + radius * std::sin(endRad);
  return endPoint;
}

FanParts Fan::get_parts() {
  auto poly = to_polygon();
  auto v_points = connect_end(poly->points);
  auto v_edges = get_edges(v_points);
  auto v_edges_mid = get_edges_mid(v_edges);

  auto box = bounding_box();
  auto box_points = box_to_points(box);
  auto box_edges = get_edges(box_points);
  auto box_edges_mid = get_edges_mid(box_edges);

  auto arc_start = poly->points[1];
  auto arc_end = poly->points[poly->points.size() - 1];
  // auto arc_start = get_arc_start();
  // auto arc_end = get_arc_end();
  auto arc_center = get_arc_center();

  auto radius_start = Vec2dArray{center, arc_start};
  auto radius_end = Vec2dArray{center, arc_end};

  qlib::Vec2dArray arc(poly->points.begin() + 1, poly->points.end());

  auto res = std::make_tuple(center, v_points, v_edges, v_edges_mid, box_points, box_edges, box_edges_mid, arc_center, arc_start, arc_end, arc,
                             radius_start, radius_end);
  return res;
}

Vec2dArray Fan::query_parts(GeoPartType type, uint32_t id) {
  auto parts = get_parts();
  if (type == GeoPartType::Center)
    return Vec2dArray{std::get<0>(parts)};
  if (type == GeoPartType::Point)
    return Vec2dArray{std::get<1>(parts)[id]};
  if (type == GeoPartType::EdgeMid)
    return Vec2dArray{std::get<2>(parts)[id]};
  if (type == GeoPartType::Edge)
    return std::get<3>(parts);
  if (type == GeoPartType::BoxPoint)
    return Vec2dArray{std::get<4>(parts)[id]};
  if (type == GeoPartType::BoxEdge)
    return std::get<5>(parts)[id];
  if (type == GeoPartType::BoxEdgeMid)
    return Vec2dArray{std::get<6>(parts)[id]};
  if (type == GeoPartType::ArcCenter)
    return Vec2dArray{std::get<7>(parts)};
  if (type == GeoPartType::ArcStart)
    return Vec2dArray{std::get<8>(parts)};
  if (type == GeoPartType::ArcEnd)
    return Vec2dArray{std::get<9>(parts)};
  if (type == GeoPartType::Arc)
    return std::get<10>(parts);
  if (type == GeoPartType::RadiusStart)
    return std::get<11>(parts);
  if (type == GeoPartType::RadiusEnd)
    return std::get<12>(parts);
  return Vec2dArray{};
}

Vec2dArray Fan::render_aabb() {
  return bounding_box();
}

Vec2dArray Fan::render_poly() {
  auto p = to_polygon();
  box = p->bounding_box();
  return p->points;
}

Vec2dArray Fan::render_absorp() {
  auto p = to_polygon();
  box = p->bounding_box();
  return p->points;
}

double Fan::getCenterAngle() {
  if (angle.y > angle.x)
    center_angle = angle.y - angle.x;
  else
    center_angle = angle.x - angle.y;
  return center_angle;
}
void Fan::setCenterAngle(double _angle) {
  center_angle = _angle;
  angle.y = angle.x + center_angle;
}

int Fan::getPointNum() {
  return point_num;
}
void Fan::setPointNum(int num) {
  point_num = num;
}

double Fan::getRotation() {
  return rotation;
}
void Fan::setRotation(double _rotation) {
  rotation = _rotation;
}

double Fan::area() const {
  return (M_PI * radius * radius) * (center_angle / (2 * M_PI));
}

void Fan::transform(double magnification, bool x_reflection, double rotation, Vec2d origin) {
  auto poly = to_polygon();
  poly->transform(magnification, x_reflection, rotation, origin);

  if (!poly->points.empty()) {
    center = poly->points[0];

    radius *= magnification;
    center *= magnification;
  }
}

}  // namespace qlib